package main

import (
	"flag"
	"fmt"
	"image"
	"image/draw"
	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"
	"os"
	"runtime/pprof"

	p9 "code.google.com/p/jnj.plan9/draw"
	"code.google.com/p/jnj/cmd/util"

	"github.com/nfnt/resize"
)

var die = util.Die

var (
	disp         *p9.Display
	originalImg  *image.RGBA                 // the original unresized image
	currentImg   *image.RGBA                 // the decoded image, at the current chosen scale
	resizedImgs  = make(map[int]*image.RGBA) // a cache of resized images by scale factor
	croppedImg   *image.RGBA                 // the image after scrolling and cropping
	scalePercent = 100
	scroll       image.Point

	cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
)

func main() {
	flag.Parse()
	if len(flag.Args()) != 1 {
		fmt.Fprintln(os.Stderr, "Usage:", os.Args[0], "file")
		os.Exit(1)
	}

	f, err := os.Open(flag.Arg(0))
	die(err)

	disp, err = p9.Init(nil, "view", "800x600")
	die(err)
	defer disp.Close()
	croppedImg = image.NewRGBA(disp.ScreenImage.Bounds())

	decoded, _, err := image.Decode(f)
	die(err)

	var ok bool
	if originalImg, ok = decoded.(*image.RGBA); !ok {
		originalImg = image.NewRGBA(decoded.Bounds())
		draw.Draw(originalImg, decoded.Bounds(), decoded, image.ZP, draw.Src)
	}

	currentImg = originalImg
	resizedImgs[100] = currentImg
	scaleToFit()
	redraw()
	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		die(err)
		pprof.StartCPUProfile(f)
	}

	handleInput()
}

func scaleToFit() {
	for currentImg.Bounds().In(disp.ScreenImage.Bounds()) == false {
		scaleDown()
		if scalePercent == 5 {
			break
		}
	}
}

func scaleUp() {
	if scalePercent >= 100 {
		scaleBy(20)
	} else if scalePercent >= 50 {
		scaleBy(10)
	} else {
		scaleBy(5)
	}
}

func scaleDown() {
	if scalePercent > 100 {
		scaleBy(-20)
	} else if scalePercent > 50 {
		scaleBy(-10)
	} else if scalePercent > 10 {
		scaleBy(-5)
	} else {
		scalePercent = 5
		scaleBy(0)
	}
}

func scaleBy(step int) {
	scalePercent += step
	scaleFactor := float64(scalePercent) / 100.0
	if cached, ok := resizedImgs[scalePercent]; ok {
		currentImg = cached
	} else {
		w := uint(float64(originalImg.Bounds().Dx()) * scaleFactor)
		currentImg = resize.Resize(w, 0, originalImg, resize.NearestNeighbor).(*image.RGBA)
		resizedImgs[scalePercent] = currentImg
	}
}

func handleInput() {
	mc := disp.InitMouse()
	kc := disp.InitKeyboard()
	dirty := false
	dragging := false
	lastDrag := image.ZP
	for {
		select {
		case ke := <-kc.C:
			switch ke {
			// esc
			case 27:
				pprof.StopCPUProfile()
				return
			case 43:
				scaleUp()
				dirty = true
			case 45:
				scaleDown()
				dirty = true
			// up
			case 128:
				scroll.Y -= 30
				dirty = true
			// down
			case 61454:
				scroll.Y += 30
				dirty = true
			// right
			case 61457:
				scroll.X += 30
				dirty = true
			// left
			case 61458:
				scroll.X -= 30
				dirty = true
			}
			//fmt.Println(ke)
		case me := <-mc.C:
			if dragging == false && me.Buttons == 1 {
				dragging = true
				lastDrag = me.Point
				break
			}
			if dragging {
				if me.Buttons != 1 {
					dragging = false
					break
				}
				scroll = scroll.Sub(me.Point.Sub(lastDrag))
				dirty = true
				lastDrag = me.Point
			}
			//fmt.Println(me.Buttons)
		}
		if dirty {
			redraw()
			dirty = false
		}
	}
}

func redraw() {
	draw.Draw(croppedImg, croppedImg.Bounds(), image.White, image.ZP, draw.Src)
	draw.Draw(croppedImg, croppedImg.Bounds(), currentImg, scroll, draw.Src)
	_, err := disp.ScreenImage.Load(croppedImg.Bounds(), croppedImg.Pix)
	die(err)
	disp.Flush()
}
